package problems;


public abstract class AbstractThreadedEuler extends AbstractEuler {
	
	private static final int CORES = 4;
	private static final boolean HYPERTHREADING = true;
	
	private static final int NUMBER_OF_THREADS = HYPERTHREADING ? CORES * 2 : CORES;
	
	protected long counter = getStartValue();
	
	@Override
	public Number calculate() {
		Thread t = new DispatcherThread();
		t.setName("looper");
		t.start();
		try {
			t.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		Number finalAnswer = getFinalAnswer();
		return finalAnswer;
	}

	protected class DispatcherThread extends Thread {

		@Override
		public void run() {
			Thread[] calculatorThreads = new Thread[NUMBER_OF_THREADS];

			loop:
			while (true) {
				for (int t = 0; t < NUMBER_OF_THREADS; t++) {
					if (continueCalculating()) {
						if (calculatorThreads[t] == null || !calculatorThreads[t].isAlive()) {
							calculatorThreads[t] = new CalculatorThread(counter);
							calculatorThreads[t].setName(t + ": " + counter);
							calculatorThreads[t].start();
							counter++;
						} 
					} else {
						break loop;
					}
				}
			}

			for (int t = 0; t < NUMBER_OF_THREADS; t++) {
				try {
					Thread thread = calculatorThreads[t];
					if (thread != null) thread.join();
				} catch (InterruptedException e) {
					fail("no interruptions!");
				}
			}

		}

	}

	private class CalculatorThread extends Thread {

		long ourValue;

		CalculatorThread(long param) {
			this.ourValue = param;
		}

		@Override
		public void run() {
			doCalculation(ourValue);
		}
	}
	
	
	abstract protected long getStartValue();
	abstract protected void doCalculation(long forValue);
	abstract protected Number getFinalAnswer();
	abstract protected boolean continueCalculating();
	
}
